﻿using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Castle.DynamicProxy;
using Extensions.Attribute.Cache;
using Extensions.Interceptor.Cache.Keys;

namespace Extensions.Interceptor.Cache;
// 采用模板方法  具体的实现由子类去实现  父类搭建框架
public abstract class BaseCacheInterceptor : IInterceptor
{
    private readonly KeyContext _keyContext;
    // 因为这个类没有注入  所以不能用属性注入  由子类来注入 context
    public BaseCacheInterceptor(KeyContext keyContext)
    {
        _keyContext = keyContext;
    }
    // iinterceptor接口的规范  由子类去实现
    public  void Intercept(IInvocation invocation)
    {
        //方法执行之前
        // 获取当前方法的类型   target是代理的接口实现的方法  method是当前代理的方法 如果代理的是接口 那就是接口
        var method = invocation.MethodInvocationTarget ?? invocation.Method;
        var cacheAttribute =
            method.GetCustomAttributes(true).FirstOrDefault(p => p is BaseCacheAttribute) as BaseCacheAttribute;
        // 如果没有标签那么就直接执行接下来的 拦截器  或者方法
        if (cacheAttribute == null || string.IsNullOrWhiteSpace(cacheAttribute.Action.ToString())||string.IsNullOrWhiteSpace(cacheAttribute.Key))
        {
            invocation.Proceed();
            return;
        }
        var key = GetKey(invocation,cacheAttribute.Key);
        // 如果缓存中存在 那么就在缓存中获取  由于删除是后置的  所以 多个标签的时候 不会发生冲突
        //var existValue = false;// 缓存中是否存在值
        if (cacheAttribute.Action == CacheActionEnum.GetOrCreate)
        {
            if (!IsVoidOrTaskReturnType(method))
            {
                var existValue = Get(key, invocation);
                if (existValue)
                {
                    return;
                }
            }
            // 如果存在缓存  那么就从缓存中获取
        }
        // 如果 缓存中存在  那么就不执行 接下来的aop和method了

        invocation.Proceed();
        // 如果缓存中不存在 时才会走到这边 那么  就在这边写入缓存
        if (cacheAttribute.Action == CacheActionEnum.GetOrCreate)
        {
            if (!IsVoidOrTaskReturnType(method))
            {
                // 传下去是object 然后要判断是否为null  在各自的实现中
                Set(key, invocation);
            }
        }
        // 后置执行删除 
        if (cacheAttribute.Action == CacheActionEnum.Delete)
        {
            Delete(key);
        }
    }


    /// <summary>
    /// 判断 方法是不是没有返回值
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    private bool IsVoidOrTaskReturnType(MethodInfo method)
    {
        ArgumentNullException.ThrowIfNull(method);
        return method.ReturnType == typeof(void) || method.ReturnType == typeof(Task);
    }
    /// <summary>
    /// 判断类型是不是task类型
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    private bool IsTaskType(MethodInfo method)
    {
        return typeof(Task).IsAssignableFrom(method.ReturnType);
    }
    /// <summary>
    /// 获取方法的返回值
    /// </summary>
    /// <param name="invocation"></param>
    /// <returns></returns>
    protected object GetReturnValue(IInvocation invocation)
    {
        object returnValue;
        var type = invocation.Method.ReturnType;
        if (typeof(Task).IsAssignableFrom(type))
        {
            var resultProperty = type.GetProperty("Result");
            returnValue = resultProperty.GetValue(invocation.ReturnValue);
        }
        else
        {
            returnValue = invocation.ReturnValue;
        }

        return returnValue;
    }
    /// <summary>
    /// 获取方法的返回类型
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    protected Type GetReturnType(MethodInfo method)
    {
        Type returnType;
        // 判断是不是task类型的
        if (IsTaskType(method))
        {
            // 默认 返回的最外层的泛型 只有  只有一个参数
            returnType = method.ReturnType.GenericTypeArguments[0];
        }
        else
        {
            returnType = method.ReturnType;
        }

        return returnType;
    }
    /// <summary>
    /// 从缓存中获取
    /// </summary>
    /// <param name="key"></param>
    /// <param name="invocation"></param>
    /// <returns></returns>
    protected abstract bool Get(string key,IInvocation invocation);
    /// <summary>
    /// 从缓存中删除
    /// </summary>
    /// <param name="key"></param>
    protected abstract void Delete(string key);
    /// <summary>
    ///  写入缓存
    /// </summary>
    /// <typeparam name=""></typeparam>
    /// <param name="key"></param>
    /// <param name="invocation"></param>
    protected abstract void Set(string key, IInvocation invocation);

    /// <summary>
    /// 根据attribute 解析出key
    /// </summary>
    /// <param name="invocation"></param>
    /// <param name="keyExpression"></param>
    /// <returns></returns>
    protected string GetKey(IInvocation invocation,string keyExpression)
    {
        return _keyContext.CreateKey(keyExpression,invocation);
    }

}